
#include "shader.h"
#include "render.h"
#include "graphics.h"
#include "glext.h"
#include <Cg/cgGL.h>

using namespace WtfEngine;

/**** Global Data ****/

CGcontext	ShaderBase::gCgContext;
CGprofile	ShaderBase::gCgGeometryProfile, ShaderBase::gCgVertexProfile,
	ShaderBase::gCgFragmentProfile;
bool		ShaderBase::gCgInitialized = false;


/**** ShaderBase ****/

CGprogram ShaderBase::LoadProgram(tString sName, tString sShaderSrc, int shaderType, bool bFp){
	CGprogram	prog;
	if(shaderType & FROM_FILE){
		prog =
		cgCreateProgramFromFile(
		  gCgContext,              /* Cg runtime context */
		  CG_SOURCE,                /*   Program in human-readable form */
		  sShaderSrc,  /* Name of file containing program */
		  (bFp ? gCgFragmentProfile : gCgVertexProfile),
		  sName,      /* Entry function name */
		  NULL);
		CheckCgError("creating shader from file");
	}else{
		throw "TODO";
	};

	cgGLLoadProgram(prog);
	CheckCgError("loading shader");

	return prog;
};

void ShaderBase::Create(tString sVp,tString sFp, tString sShaderSrc, int shaderType)
{
	//Delete();
	Init();

	if(!(shaderType & SOURCE_CODE)) throw "TODO";

	mCgVProgram = sVp ? LoadProgram(sVp, sShaderSrc, shaderType, false) : NULL;
	mCgFProgram = sFp ? LoadProgram(sFp, sShaderSrc, shaderType, true) : NULL;
};

CGparameter	ShaderBase::GetParam(tString sName, bool bFp){
	CGparameter p = cgGetNamedParameter(bFp ? mCgFProgram : mCgVProgram, sName);
	CheckCgError("could not get parameter");
	return p;
};

void ShaderBase::Init(){
	if(!gCgInitialized){
		gCgContext = cgCreateContext();
		CheckCgError("creating context");
		cgGLSetDebugMode( CG_FALSE );
		cgSetParameterSettingMode(gCgContext, CG_DEFERRED_PARAMETER_SETTING);

		/*gCgVertexProfile = cgGLGetLatestProfile(CG_GL_GEOMETRY);
		cgGLSetOptimalOptions(gCgGeometryProfile);
		CheckCgError("selecting geometry profile");*/

		gCgVertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);
		cgGLSetOptimalOptions(gCgVertexProfile);
		CheckCgError("selecting vertex profile");

		gCgFragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);
		cgGLSetOptimalOptions(gCgFragmentProfile);
		CheckCgError("selecting fragment profile");
	};
};

void ShaderBase::Enable()
{
	checkGLError("starting enabling shader");
	if(mCgFProgram){
		cgGLBindProgram(mCgFProgram);
		CheckCgError("binding fragment program");
		cgGLEnableProfile(gCgFragmentProfile);
		CheckCgError("enabling fragment profile");
		cgUpdateProgramParameters(mCgFProgram);
	}
	if(mCgVProgram){
		cgGLBindProgram(mCgVProgram);
		CheckCgError("binding vertex program");
		cgGLEnableProfile(gCgVertexProfile);
		CheckCgError("enabling vertex profile");
		cgUpdateProgramParameters(mCgVProgram);
	};
	checkGLError("finished enabling shader");
};

void ShaderBase::Disable()
{
	cgGLDisableProfile(gCgVertexProfile);
    cgGLDisableProfile(gCgFragmentProfile);
};

VertexLighting::VertexLighting()
	: ShaderBase("cg_lighting","cg_lighting", NULL, "src/shaders/lighting.cg",
		SOURCE_CODE | FROM_FILE)
{
	// Get parameter handles
	modelViewMatrix = GetParam("modelViewProj");
	globalAmbient = GetParam("globalAmbient");
	lightColor = GetParam("lightColor");
	lightPosition = GetParam("lightPosition");
	eyePosition = GetParam("eyePosition");
	Ke = GetParam("Ke");
	Ka = GetParam("Ka");
	Kd = GetParam("Kd");
	Ks = GetParam("Ks");
	shininess = GetParam("shininess");


	tScalar f[3] = { 0.0, 0.0, 0.0 };  /* Dim */
	tScalar c[3] = { 0.2, 0.2, 0.2 };  /* Some colour */
	cgSetParameter3fv(globalAmbient, f);
	cgSetParameter3fv(lightColor, c);

	const tScalar brassEmissive[3] = {0.0,  0.0,  0.0},
      brassAmbient[3]  = {0.33, 0.22, 0.03},
      brassDiffuse[3]  = {0.78, 0.57, 0.11},
      brassSpecular[3] = {0.99, 0.91, 0.81},
      brassShininess = 1.8;

	  cgSetParameter3fv(Ke, brassEmissive);
	  cgSetParameter3fv(Ka, brassAmbient);
	  cgSetParameter3fv(Kd, brassDiffuse);
	  cgSetParameter3fv(Ks, brassSpecular);
	  cgSetParameter1f(shininess, brassShininess);
}

void VertexLighting::Update()
{
	const tScalar p[3] = {200,  400,  400};
	cgSetParameter3fv(lightPosition, p);

};

HdrDownsampler::HdrDownsampler()
	: ShaderBase("cg_hdr_downsample", "cg_hdr_downsample_vp", "cg_hdr_downsample",
		"src/shaders/hdr_downsample.cg", SOURCE_CODE | FROM_FILE)
{
	// Get parameter handles
	mparamGamma = GetParam("gamma", true);
	mparamExposure = GetParam("exposure", true);
	cgSetParameter1f(mparamGamma, 0.9f);
}

void HdrDownsampler::SetExposure(tScalar e){
	cgSetParameter1f(mparamExposure, e);
}

HdrExposure::HdrExposure()
	: ShaderBase("cg_hdr_exposure", NULL, "cg_hdr_exposure",
		"src/shaders/hdr_downsample.cg", SOURCE_CODE | FROM_FILE)
{
	
}

